package com.zwan.oam_rtc.imps;

import com.zwan.oam_rtc.constants.Const;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Iterator;



@Slf4j
public class ServerSocketHandler implements Runnable {

	private ServerSocket serverSocket;
	private ArrayList<String> allowip = new ArrayList<String>();
	private ArrayList<String> deniedip = new ArrayList<String>();
	private InetSocketAddress addr;
	private int maxconnect;
	
	public ServerSocketHandler(){
		this.init();
	}
	
	public void init(){

//		int port = Integer.parseInt(serverEle.attributeValue("bindport", "0"));
//		if (port >= 0)
//		{
//			if (serverEle.attributeValue("bindip") != null)
//			{
//				addr = new InetSocketAddress(serverEle.attributeValue("bindip"), port); 
//			}
//			else
//				addr = new InetSocketAddress(port);
//			for (Iterator<Element> iter = serverEle.elementIterator("AllowIP"); iter.hasNext();) {
//				Element el = iter.next();
//				String ip = el.getTextTrim();
//				if (ip.length() > 0){
//					if (!el.attributeValue("mask" , "").equals(""))
//						ip += "," + el.attributeValue("mask");
//					allowip.add(ip);
//				}
//			}
//			for (Iterator<Element> iter = serverEle.elementIterator("DeniedIP"); iter.hasNext();) {
//				Element el = iter.next();
//				String ip = el.getTextTrim();
//				if (ip.length() > 0){
//					if (!el.attributeValue("mask" , "").equals(""))
//						ip += "," + el.attributeValue("mask");
//					deniedip.add(ip);
//				}
//			}
//		}
//		maxconnect = Math.min(Integer.parseInt(serverEle.attributeValue("maxconnect", "10")), Const.MAX_SOCKET_CLIENT_NUM);
	}
	
	public void save(){
//		Element ele = parentEle.element("Server");
//		if (ele == null)
//			ele = parentEle.addElement("Server");
//		
//		ele.clearContent();
//		ele.attributes().clear();
//		ele.addAttribute("maxconnect", String.valueOf(maxconnect));
//		ele.addAttribute("bindport", String.valueOf(serverSocket.getLocalPort()));
//		ele.addAttribute("bindip", serverSocket.getInetAddress().getHostAddress());
//		for (Iterator<String> iterator = allowip.iterator(); iterator.hasNext();) {
//			String ip = iterator.next();
//			String[] ipvalue = ip.split(",");
//			Element ipEle = ele.addElement("AllowIP"); 
//			if (ipvalue.length >= 2)
//				ipEle.addAttribute("mask", ipvalue[1]);
//			ipEle.setText(ipvalue[0]);
//		}
//		for (Iterator<String> iterator = deniedip.iterator(); iterator.hasNext();) {
//			String ip = iterator.next();
//			String[] ipvalue = ip.split(",");
//			Element ipEle = ele.addElement("DeniedIP"); 
//			if (ipvalue.length >= 2)
//				ipEle.addAttribute("mask", ipvalue[1]);
//		}
	}
	
	public void run() {
		if (addr == null || addr.getPort() == 0) return;
		if (serverSocket != null && serverSocket.isBound()) return ;
		try {
			serverSocket = new ServerSocket();
			serverSocket.bind(addr);
			log.info("ServerSocket is listen for " + addr.toString());
		} catch (Exception e1) {
			log.info(MessageFormat.format("Bind server [{0}] failed: {1}", addr.toString(), e1.getMessage()));
			return;
		}
		while(true){
			InetAddress remote = null;
			try {
				//程序将在这里阻塞，除非有客户端socket向服务端发起连接，则服务端就新建一个socket与客户端的socket对应，然后继续执行
				Socket socket = serverSocket.accept();  
				socket.setSoTimeout(Const.TIME_IDLE * 2);  //设置连接处理超时为10秒，10秒后抛异常
				remote = socket.getInetAddress();//获得客户端的ip和port
				if (JavaImp.getInstance().getServerHandlers().size() >= maxconnect){
					log.info("Connection pool is full, close connection of " + remote.toString());
					socket.close();
					continue;
				}
				String ip = remote.getHostAddress(); //客户端的ip
				
				if (!checkIPLimit(allowip, deniedip, ip, remote.getAddress())){
					socket.close();
					log.info(MessageFormat.format("Client ip [{0}] connect not allowed", ip));
					continue;
				}
				//服务器启动一个线程来处理刚得到的socket，这个线程持有该socket与该客户端的socket保持联系
				/**
				 * 每个客户端连接过来后，服务端都会启动一个线程去处理该客户端的请求。并且多线程处理多个连接。每个线程拥有自己的
				 * 栈空间并且占用一些 CPU 时间。每个线程遇到外部未准备好的时候，都会阻塞掉。阻塞的结果就是会带来大量的进程上下
				 * 文切换。且大部分进程上下文切换可能是无意义的。比如假设一个线程监听某一个端口，一天只会有几次请求进来，但是该 
				 * cpu 不得不为该线程不断做上下文切换尝试，大部分的切换以阻塞(很多客户端可能一天就几个消息，线程一直处于阻塞状态)告终。
				 */
				JavaImp.getInstance().getExecutor().submit(new ServerHandler(socket));
			} catch (IOException e) {
				if (remote != null)
					log.info(MessageFormat.format("Connection from {0} closed: ", remote.toString(), e.getMessage()));
				else
					log.info(e.toString());
			} catch(Exception e){
				log.info(e.toString());
			}
			
		}
	}
	
	private boolean checkIPLimit(ArrayList<String> allowip, ArrayList<String> deniedip, String ip, byte[] ipbytes){
		// 0 特例，黑名单和白名单都为空，允许
		if (deniedip.isEmpty() && allowip.isEmpty()) return true;
		// 1、在黑名单中直接存在的，不允许
		if (deniedip.indexOf(ip) >= 0) return false;
		// 2、在白名单中直接存在，但不在黑名单中直接存在的，允许
		if (allowip.indexOf(ip) >= 0) return true;
		// 3、除以上条件，能被黑名单匹配的，不允许
		for (Iterator<String> iterator = deniedip.iterator(); iterator.hasNext();) {
			String dip = iterator.next();
			if (dip.indexOf('*') <= 0) break;
			if (checkIPMatch(dip, ipbytes)) return false;			
		}
		// 4、除以上条件，可以被白名单匹配，但不能被黑名单匹配的，允许
		for (Iterator<String> iterator = allowip.iterator(); iterator.hasNext();) {
			String dip = iterator.next();
			if (dip.indexOf('*') <= 0) break;
			if (checkIPMatch(dip, ipbytes)) return true;			
		}
		// 其它，默认都不允许
		return false;
	}

	// 暂时不支持ipv6

	private static boolean checkIPMatch(String dip, byte[] ipbytes) {
		if (ipbytes.length == 4){
			// TODO 处理掩码 mask
//			byte[] mask = new byte[4];
//			if (dip.indexOf(",") >= 0)
//			{
//				
//			}
			String[] dips = dip.split(",")[0].split("\\.");
			for (int i = 0; i < Math.min(dips.length, ipbytes.length); i++) {
				if (dips[i].indexOf(',') >= 0)
				{
				    if (dips[i].equals("*"))
				        continue;
				}
				else if (ipbytes[i] != (byte)Short.parseShort(dips[i]))
					return false;
			}
			return true;
		}
		else{
//			log.error("Not yet support IPv6.");
			System.out.println("Not yet support IPv6.");
			return false;
		}
	}
	
	// test checkIPMatch
//	public static void main(String[] args){
//		byte[] ip = {0x0A, 0x0A, 0x0A, 0x0A};	// 10.10.10.10
//		String[] masks = {"*.*.*.*","10.*.*.*", "10.130.*.*", "10.*", "10", "10.*.*.*.10", "10.*.*.10", "*.*.10", "*.10.10.*"};
//		for (int i = 0; i < masks.length; i++) {
//			System.out.print(masks[i]);
//			System.out.print("\t");
//			System.out.println(checkIPMatch(masks[i], ip));
//		}
//	}
}
